﻿using System;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using AIthin.Neuro;
using AIthin.Model.Body;
using AIthin.Model;


namespace AIthin.Controls
{
    /// <summary>
    /// Class NeuroControl to interact with NeuroNetwork
    /// </summary>
    public partial class NeuroControl : UserControl, INeuroControl
    {
        /// <summary>
        /// Unique network name for this network
        /// </summary>
        public String NetworkName { get; set; }
        /// <summary>
        /// interacted network
        /// </summary>
        public INetwork network = null;
        Reality R;
        Wurm K;

        float shoots = 0;
        float hits = 0;

        /// <summary>
        /// stream to learn
        /// </summary>
        LearnStream learnStream;
        /// <summary>
        /// Researchresult database
        /// </summary>
        RDBExpressDataSet DB;
        /// <summary>
        /// all input signals
        /// </summary>
        short[][,] learnInput = null;
        /// <summary>
        /// all output signalls
        /// </summary>
        short[][] learnOutput = null;
        short[,] inputFrame = null;
        /// <summary>
        /// current tick count
        /// </summary>
        int TickCount = 0;
        Bitmap bmRealitySnap;
        public String WorkingPath { get; set; }
        /// <summary>
        /// definition for the event 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public delegate void TickComputed(object sender, string e);
        /// <summary>
        /// send event when one tick will be computed
        /// </summary>
        public event TickComputed networkTickComputed;
        public NeuroControl()
        {
            InitializeComponent();
            learnStream = new LearnStream();

            Name = "NC";

            this.SetStyle(0
                              | ControlStyles.OptimizedDoubleBuffer //DoubleBuffer
                              | ControlStyles.UserPaint
                              | ControlStyles.AllPaintingInWmPaint
                              , true);

            this.UpdateStyles();

            labelLearnFile.Text = "";
            buttonStopLearn.Enabled = false;
            DB = new RDBExpressDataSet();
            R = new Squash(64, 128);
            bmRealitySnap = new Bitmap(R.Width, R.Hight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

        }
        /// <summary>
        /// create control from file
        /// </summary>
        /// <param name="Path">Path to  control without slash</param>
        /// <param name="FileName">Filename with extention</param>
        public NeuroControl(String Path, String FileName) : this()
        {
            LoadNetworkFile(Path, FileName);
            K = new Wurm(network, R);
        }
        /// <summary>
        /// load network file *.nw1
        /// </summary>
        /// <param name="Path">file path without last slash \</param>
        /// <param name="FileName">file name with end suffix </param>
        public void LoadNetworkFile(String Path, String FileName)
        {
            String FullFileName = Path + "\\" + FileName;
            network = new Network();

            if (File.Exists(FullFileName))
            {
                network = network.Load(FullFileName);
                NetworkName = FileName.Remove(FileName.LastIndexOf('.'));

            }
            K = new Wurm(network, R);

        }
        private bool loadLearnFile()
        {
            openLearnFileDialog.ShowDialog();
            String learnFileName = openLearnFileDialog.FileName;

            if (!learnStream.GetLearnSet(learnFileName, out learnInput, out learnOutput))
            {
                MessageBox.Show("File doesn´t exist");
                return false;
            }

            labelLearnFile.Text = "..." + learnFileName.Substring(learnFileName.Length - 52);
            this.Invalidate();
            return true;
        }
        private bool openLearnFile()
        {
            openLearnFileDialog.ShowDialog();
            String learnFileName = openLearnFileDialog.FileName;

            if (!learnStream.open(learnFileName))
            {
                MessageBox.Show("File doesn´t exist");
                return false;
            }

            labelLearnFile.Text = "..." + learnFileName.Substring(learnFileName.Length);
            this.Invalidate();
            return true;
        }
        /// <summary>
        /// save network file *.nw1
        /// </summary>
        /// <param name="Path">file path without last slash \</param>
        /// <param name="FileName">file name with end suffix .nw</param>
        public void SaveNetworkFile(String Path)
        {
            network.Save(Path + "\\"
                + NetworkName + "_"
                + network.LifeTime.ToString()
                + ".nw1");

            MessageBox.Show(" Network "
                + NetworkName + "_"
                + network.LifeTime.ToString()
                + ".nw1" + " saved.");
        }
        protected override void OnPaint(PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.DrawImage(bmRealitySnap, 10, 20);
        }
        /// <summary>
        /// make a epoch or tick write data to control
        /// </summary>
        /// <param name="k">tick count</param>
        private void makeATick(int k)
        {
            labelLearnCycles.Text = TickCount.ToString();
            labelNeuronsCount.Text = K.mind.NeuronsCount.ToString();
            labelEmbryosCount.Text = K.mind.getConnectionsCount().ToString();

            K.Reaction(ref bmRealitySnap);

            labelNeuronsCount.Text = network.NeuronsCount.ToString();

            if (networkTickComputed != null) networkTickComputed(this, "tick computed");

            Invalidate();
            Application.DoEvents();
        }
        private void buttonStep_Click(object sender, EventArgs e)
        {
            if (TickCount < learnStream.LearnSets)
            {
                bmRealitySnap = learnStream.getNextFrame(1);

                makeATick(TickCount++);

                if (K.checkHit()) ballHit();

            }

        }
        private void buttonStopLearn_Click(object sender, EventArgs e)
        {
            buttonStopLearn.Enabled = false;
        }
        private void viewToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }
        private void startEvolutionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            buttonStopLearn.Enabled = true;

            TickCount = 0;
            shoots = 0;
            hits = 0;

            while (TickCount++ < 1300000 & buttonStopLearn.Enabled)
            {
                if (R.Moving())
                {
                    shoots += 1f;
                    K.muscle.middle();
                }
                // erstellt neues Schreiben in Bild
                Graphics g = Graphics.FromImage(bmRealitySnap);

                R.Draw(g);

                makeATick(TickCount++);

                if (R.GetBallPositionY() == 8)
                    if (R.GetBallPositionX() > K.muscle.Position &
                            R.GetBallPositionX() < K.muscle.Position + K.muscle.Length)
                    {
                        ballHit();
                        K.mind.learn(0.05f);
                    }
                    else
                        K.mind.learn(-0.05f);

            }
        }
        private void ballHit()
        {
            
                labelMaxOutput.Text = K.damageCapacity.ToString();
                labelMaxOutput.Refresh();

                hits += 1f;
                successRate.Text = (hits / shoots).ToString("P");
                successRate.Refresh();


                // wenn tod
                if (K.IsHit())
                {
                    //altes Speichern
                    K.mind.Save(WorkingPath + "\\wu32_"
                        + K.mind.LifeTime.ToString() + "-"
                        + (hits / shoots).ToString("P")
                        + ".nw1");
                    //neues erstellen
                    network = (INetwork)new Network(96, 1024, 24);
                    K = new Wurm(network,R);

                    R.ShootBall();

                    shoots = 1;
                    hits = 0;

                }
        }
        private void learnToolStripMenuItem_Click(object sender, EventArgs e)
        {
            buttonStopLearn.Enabled = true;
            if (!openLearnFile())
            {
                MessageBox.Show("Cannt open learn file!");
                return;
            }
            //TODO does it work?
            Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;
            // Thread.CurrentThread.Name = "Um";

            TickCount = 0;
            shoots = 0;
            hits = 0;

            while (TickCount < learnStream.LearnSets & buttonStopLearn.Enabled)
            {

                bmRealitySnap = learnStream.getNextFrame(1);

                makeATick(TickCount++);

                if (K.checkHit()) ballHit();

            }
        }

    }
}
